import { LoginStateItem, Props } from './interface'
import { createHash } from 'crypto'
import { IncomingMessage, OutgoingHttpHeaders, request, RequestOptions, ServerResponse } from 'http';
import { URL } from 'url'

export const AUTH_KEY = 'F2E_AUTH'
export const PLACE_HOLDER = '$[placeholder]'

export const generateID = (items: Props[]) => Math.max.apply(Math, items.map(({id}) => id)) + 1

export const MD5 = (seed: string) => createHash('md5').update(seed || '').digest('hex')
export const btoa = (str: string) => Buffer.from(str).toString('base64')
export const atob = (str: string) => Buffer.from(str, 'base64').toString()
export const generateToken = () => MD5(Date.now().toString())
export const getURLSearchParams = (req: IncomingMessage) => {
    const { searchParams } = new URL(req.url || '', 'http://127.0.0.1')
    return searchParams
}

/**
 * @getClientIP
 * @desc 获取用户 ip 地址
 * @param {Object} req - 请求
 */
export const getClientIP = (req: any) => {
    let ip = req.headers['x-forwarded-for'] || // 判断是否有反向代理 IP
        req.connection.remoteAddress || // 判断 connection 的远程 IP
        req.socket.remoteAddress || // 判断后端的 socket 的 IP
        req.connection.socket.remoteAddress;

    let ips = ip.match(/\d+.\d+.\d+.\d+/);
    return ips && ips[0]
}

const login_state_map = new Map<string, LoginStateItem>()
export const getLoginState = (req: any): LoginStateItem => {
    const IP = getClientIP(req)
    const state: LoginStateItem = login_state_map.get(IP) || {
        IP, crsf_token: generateToken(),
        error_count: 0,
    };
    login_state_map.set(IP, state)
    return state
}

export interface CookieOptions {
    maxAge?: number
    domain?: string
    path?: string
    expires?: Date
    httpOnly?: boolean
    secure?: boolean
    sameSite?: 'Strict' | 'Lax'
}
export const Cookie = {
    parse (cookie?: string) {
        let cookies: {[k: string]: string} = {}
        if (!cookie) {
            return cookies
        }
        const list = cookie.split(';')
        for (let i = 0; i < list.length; i++) {
            const [name, value] = list[i].split('=')
            cookies[name.trim()] = value
        }
        return cookies
    },
    stringify (name: string, val: string, opt: CookieOptions) {
        var pairs = [name + '=' + encodeURI(val)]
        opt = opt || {}
        if (opt.maxAge) pairs.push('Max-Age=' + opt.maxAge)
        if (opt.domain) pairs.push('Domain=' + opt.domain)
        if (opt.path) pairs.push('Path=' + opt.path)
        if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString())
        if (opt.httpOnly) pairs.push('HttpOnly')
        if (opt.secure) pairs.push('Secure')
        pairs.push(`SameSite=${opt.sameSite || 'Lax'}`)
        return pairs.join(';')
    }
}

export const fetchData = (fullpath: string, options: RequestOptions & { token?: string, body?: any }) => new Promise<string>((resolve, reject) => {
    const { token, body } = options
    let headers: OutgoingHttpHeaders = {
        "Cache-Control": "no-cache",
        "host": fullpath.replace(/^https?:\/\/([^\/]+).*$/, '$1'),
    }
    if (token) {
        headers['Authorization'] = `Basic ${token}`
    }
    if (body) {
        headers['Content-Length'] = Buffer.byteLength(body, 'utf-8')
    }
    const req = request(fullpath, {
        ...options,
        headers: Object.assign(headers, options.headers),
    }, function (res) {
        let buffers: string[] = []
        res.setEncoding('utf8');
        res.on('data', (chunk) => {
            buffers.push(chunk)
        });
        res.on('end', () => {
            if (res.statusCode && (res.statusCode >= 200 && res.statusCode < 300)) {
                resolve(buffers.join(''))
            } else {
                reject(buffers.join(''))
            }
        });
        res.on('error', reject)
    })
    .on('error', reject);
    body && req.write(body)
    req.end();
})

export const proxy = (prefix: string, base: string, token: string) => (req: IncomingMessage, resp: ServerResponse) => {
    const newPath = new URL(req.url?.replace(prefix, '') || '', base)
    // console.log(prefix, base, req.url, newPath)
    let headers: OutgoingHttpHeaders = {
        "host": base.replace(/^https?:\/\/([^\/]+).*$/, '$1'),
    }
    if (token) {
        headers['Authorization'] = `Basic ${token}`
    }
    try {
        const creq = request(newPath, {
            method: req.method,
            headers: Object.assign({}, req.headers, headers),
        }, function (res) {
            res.pipe(resp)
        }).on('response', function (response) {
            resp.writeHead(response.statusCode || 200, response.statusMessage, response.headers)
        }).on('error', function (err) {
            resp.writeHead(500)
            resp.end(err.toString())
        });
        req.pipe(creq)
    } catch (e) {
        resp.writeHead(500)
        resp.end(e.toString())
    }
}